package org.xx.armory.swing.controllers;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xx.armory.swing.builders.ButtonModel;
import org.xx.armory.swing.builders.DropDownModel;
import org.xx.armory.swing.builders.SimpleAction;
import org.xx.armory.swing.builders.TextModel;
import org.xx.armory.swing.components.DialogPane;
import org.xx.armory.swing.components.MultiCastListener;
import org.xx.armory.swing.components.VerificationAdapter;
import org.xx.armory.swing.components.VerificationEvent;
import org.xx.armory.swing.components.VerificationListener;
import org.xx.armory.swing.components.verifiers.CurrencyVerifier;
import org.xx.armory.swing.components.verifiers.DateVerifier;
import org.xx.armory.swing.components.verifiers.FloatVerifier;
import org.xx.armory.swing.components.verifiers.InputComponentVerificationAdapter;
import org.xx.armory.swing.components.verifiers.NumberVerifier;
import org.xx.armory.swing.components.verifiers.SimpleTextVerifier;
import org.xx.armory.swing.components.verifiers.StyledInputComponentVerificationAdapter;
import org.xx.armory.swing.components.verifiers.TextVerifier;
import org.xx.armory.swing.components.verifiers.VerifiedStatus;

import javax.swing.*;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;

import static org.xx.armory.commons.Validators.notNull;
import static org.xx.armory.swing.ComponentUtils.focusInWindow;
import static org.xx.armory.swing.UIUtils.toBigDecimalObject;
import static org.xx.armory.swing.UIUtils.toDoubleObject;
import static org.xx.armory.swing.UIUtils.toLongObject;

public class DialogUIModelController
        extends PaneUIModelController<DialogPane> {
    private final Logger logger = LoggerFactory.getLogger(DialogUIModelController.class);
    private final List<InputComponentVerificationAdapter> inputVerificationAdapters;
    private MultiCastListener<VerificationListener, VerificationEvent> verificationListener;

    public DialogUIModelController(
            DialogPane dialog
    ) {
        super(dialog);

        this.verificationListener = new MultiCastListener<>();
        this.inputVerificationAdapters = new ArrayList<>();

        this.addVerificationListener(new VerificationAdapter() {
            @Override
            public void error(
                    VerificationEvent event
            ) {
                final InputComponentVerificationAdapter adapter = (InputComponentVerificationAdapter) event.getSource();
                if (adapter != null) {
                    focusInWindow(adapter.getComponent());
                }
            }
        });

        getContainer().addPropertyChangeListener(new DialogUIContainerPropertyChangeAdapter());
    }

    /**
     * 对输入元素进行验证。
     * <p>对所有的输入元素依次进行验证，即使某个元素验证失败也继续验证，只有当所有元素都验证通过时返回{@code true}。</p>
     *
     * @return 是否验证通过。
     */
    protected boolean verify() {
        InputComponentVerificationAdapter failedAdapter = null;
        VerifiedStatus failedStatus = VerifiedStatus.OK;
        for (final InputComponentVerificationAdapter adapter : this.inputVerificationAdapters) {
            final VerifiedStatus status = adapter.verify();
            if (status != VerifiedStatus.OK) {
                logger.trace("verification {} failed", adapter);
                if (failedAdapter == null) {
                    failedAdapter = adapter;
                }
                if (failedStatus == VerifiedStatus.OK) {
                    failedStatus = status;
                }
            } else {
                logger.trace("verification {} succeeded", adapter);
            }
        }

        if (failedAdapter != null) {
            this.verificationListener.invokeEach(new VerificationEvent(failedAdapter, VerificationEvent.ERROR, failedStatus), VerificationListener::error);
            return false;
        } else {
            return true;
        }
    }

    /**
     * 添加验证事件侦听器。
     *
     * @param listener
     *         验证事件侦听器。
     */
    public void addVerificationListener(
            VerificationListener listener
    ) {
        this.verificationListener.add(listener);
    }

    /**
     * 移除验证事件侦听器。
     *
     * @param listener
     *         验证事件侦听器。
     */
    public void removeVerificationListener(
            VerificationListener listener
    ) {
        this.verificationListener.remove(listener);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void connect(
            SimpleAction action,
            Consumer<ActionEvent> eventHandler
    ) {
        notNull(action, "action");

        if (action.isCauseVerification()) {
            super.connect(action, event -> {
                if (this.verify()) {
                    eventHandler.accept(event);
                }
            });
        } else {
            super.connect(action, eventHandler);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void connect(
            Component component,
            String eventName,
            Consumer<Object> eventHandler
    ) {
        switch (eventName) {
            case "verify-error":
                connectVerifyError(eventHandler);
                break;
            default:
                super.connect(component, eventName, eventHandler);
                break;
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected JTextField createSimpleText(
            TextModel model
    ) {
        final JTextField result = super.createSimpleText(model);

        if (model.isRequired() || model.getMaxLength() >= 0 || !model.getPattern().isEmpty()) {
            final TextVerifier verifier = new SimpleTextVerifier(model.isRequired(), model.getMaxLength(), model.getPattern());
            this.inputVerificationAdapters.add(new StyledInputComponentVerificationAdapter(result, verifier));
        }

        return result;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected JTextField createNumber(
            TextModel model
    ) {
        final JTextField result = super.createNumber(model);

        if (model.isRequired() || !model.getMinValue().isEmpty() || !model.getMaxValue().isEmpty()) {
            final TextVerifier verifier = new NumberVerifier(model.isRequired(), toLongObject(model.getMinValue()), toLongObject(model.getMaxValue()));
            this.inputVerificationAdapters.add(new StyledInputComponentVerificationAdapter(result, verifier));
        }

        return result;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected JTextField createFloat(
            TextModel model
    ) {
        final JTextField result = super.createFloat(model);

        if (model.isRequired() || !model.getMinValue().isEmpty() || !model.getMaxValue().isEmpty()) {
            final TextVerifier verifier = new FloatVerifier(model.isRequired(), toDoubleObject(model.getMinValue()), toDoubleObject(model.getMaxValue()), 1e-6);
            this.inputVerificationAdapters.add(new StyledInputComponentVerificationAdapter(result, verifier));
        }

        return result;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected JTextField createPercent(
            TextModel model
    ) {
        final JTextField result = super.createPercent(model);

        if (model.isRequired() || !model.getMinValue().isEmpty() || !model.getMaxValue().isEmpty()) {
            final TextVerifier verifier = new FloatVerifier(model.isRequired(), toDoubleObject(model.getMinValue()), toDoubleObject(model.getMaxValue()), 1e-6);
            this.inputVerificationAdapters.add(new StyledInputComponentVerificationAdapter(result, verifier));
        }

        return result;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected JTextField createCurrency(
            TextModel model
    ) {
        final JTextField result = super.createCurrency(model);

        if (model.isRequired() || !model.getMinValue().isEmpty() || !model.getMaxValue().isEmpty()) {
            final TextVerifier verifier = new CurrencyVerifier(model.isRequired(), toBigDecimalObject(model.getMinValue()), toBigDecimalObject(model.getMaxValue()));
            this.inputVerificationAdapters.add(new StyledInputComponentVerificationAdapter(result, verifier));
        }

        return result;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected JTextField createDate(
            TextModel model
    ) {
        final JTextField result = super.createDate(model);

        if (model.isRequired()) {
            this.inputVerificationAdapters.add(new StyledInputComponentVerificationAdapter(result, new DateVerifier(model.isRequired())));
        }

        return result;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected JTextField createDateTime(
            TextModel model
    ) {
        final JTextField result = super.createDateTime(model);

        if (model.isRequired()) {
            this.inputVerificationAdapters.add(new StyledInputComponentVerificationAdapter(result, new DateVerifier(model.isRequired())));
        }

        return result;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected JPasswordField createPassword(
            TextModel model
    ) {
        final JPasswordField result = super.createPassword(model);

        if (model.isRequired() || model.getMaxLength() >= 0 || !model.getPattern().isEmpty()) {
            final TextVerifier verifier = new SimpleTextVerifier(model.isRequired(), model.getMaxLength(), model.getPattern());
            this.inputVerificationAdapters.add(new StyledInputComponentVerificationAdapter(result, verifier));
        }

        return result;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected JComboBox<String> createComboBox(
            TextModel model
    ) {
        final JComboBox<String> result = super.createComboBox(model);

        if (model.isRequired() || model.getMaxLength() >= 0 || !model.getPattern().isEmpty()) {
            final TextVerifier verifier = new SimpleTextVerifier(model.isRequired(), model.getMaxLength(), model.getPattern());
            this.inputVerificationAdapters.add(new StyledInputComponentVerificationAdapter(result, verifier));
        }

        return result;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected JComboBox<?> createDropDown(
            DropDownModel model
    ) {
        final JComboBox<?> result = super.createDropDown(model);

        if (model.isRequired()) {
            final TextVerifier verifier = new SimpleTextVerifier(model.isRequired());
            this.inputVerificationAdapters.add(new StyledInputComponentVerificationAdapter(result, verifier));
        }

        return result;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected JButton createButton(
            ButtonModel model
    ) {
        JButton result = super.createButton(model);

        if (model.isDefault()) {
            this.getContainer().setDefaultButton(result);
        }

        return result;
    }

    protected void connectVerifyError(
            Consumer<Object> eventHandler
    ) {
        this.addVerificationListener(new VerificationAdapter() {
            @Override
            public void error(
                    VerificationEvent event
            ) {
                super.error(event);

                eventHandler.accept(event);
            }
        });
    }
}
